package com.stylefeng.guns.modular.domain_manage.service.impl;
import com.stylefeng.guns.core.common.exception.BizExceptionEnum;
import com.stylefeng.guns.core.exception.GunsException;
import com.stylefeng.guns.modular.apple_app_manage.dao.ApplicationMapper;
import com.stylefeng.guns.modular.apple_app_manage.service.IApplicationService;
import com.stylefeng.guns.modular.apple_sign_task_manage.dao.SignTasksMapper;
import com.stylefeng.guns.modular.apple_sign_task_manage.service.ISignTasksService;
import com.stylefeng.guns.modular.domain_manage.model.DomainInfo;
import com.stylefeng.guns.modular.domain_manage.dao.DomainInfoMapper;
import com.stylefeng.guns.modular.domain_manage.service.IDomainInfoService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import java.io.File;
import java.io.FileOutputStream;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * <p>
 * 域名信息表 服务实现类
 * </p>
 *
 * @author GodRui
 * @since 2020-06-29
 */
@Service
public class DomainInfoServiceImpl extends ServiceImpl<DomainInfoMapper, DomainInfo> implements IDomainInfoService {
    private final Logger logger = LoggerFactory.getLogger(DomainInfoServiceImpl.class);
    // 域名失效
    public static final Integer invalidStatus = 0;
    // 正在使用
    public static final Integer inuseStatus = 1;
    // 待用
    public static final Integer readyStatus = 2;

    @Autowired
    private IApplicationService applicationService;

    @Autowired
    private SignTasksMapper signTasksMapper;

    @Autowired
    private ApplicationMapper applicationMapper;

    @Value("${domain-pool.common-size}")
    private Integer domainPoolSize;

    @Value("${ios-app.root-path}")
    private String rootPath;

    /**
     *  获取在使用的落地页域名
     * */
    public List<DomainInfo> getInUseDomain(){
        return baseMapper.getInuseDomains();
    }

    /**
     *  获取备用域名
     * */
    public List<DomainInfo> getReadyDomains(Integer size) {
        return baseMapper.getReadyDomains(size);
    }

    @Override
    public List<DomainInfo> getDomainOrderByStatus(){
        return baseMapper.getAllDomainSortByStatus();
    }

    @Override
    public DomainInfo getOneAvailDomain(){
        Integer inUseDomainCnt = baseMapper.getInUseDomainSize();
        if(inUseDomainCnt < domainPoolSize) {
            pushReadyDomainToInUse(domainPoolSize - inUseDomainCnt);
        }
        List<DomainInfo> inUseDms = getInUseDomain();
        if(inUseDms == null || inUseDms.size() == 0) {
            throw new GunsException(BizExceptionEnum.DOMAIN_IS_EXHAUST);
        }
        Collections.shuffle(inUseDms);
        return inUseDms.get(0);
    }

    /**
     *  从备用域名中更新指定数量的域名到在使用
     * */
    public void pushReadyDomainToInUse(Integer requireSize) {
        Integer readyCount = baseMapper.getReadyDomainSize();
        baseMapper.changeRequireSizeToInuseDomain(requireSize);
    }

    /**
     *  域名更新
     * */
    @Override
    @Transactional
    public void updateDomainWithEntity(DomainInfo domainInfo) {
        DomainInfo qDomain = new DomainInfo();
        qDomain.setId(domainInfo.getId());
        DomainInfo curDomain = baseMapper.selectOne(qDomain);
        // 不存在域名
        if(curDomain == null) {
            throw new GunsException(BizExceptionEnum.DOMAIN_NOT_EXIST);
        }
        // 失效的域名不允许操作
        if(curDomain.getStatus().equals(invalidStatus)) {
            throw new GunsException(BizExceptionEnum.DOMAIN_OPERATE_NOT_SUPPORT);
        }
        if(curDomain.getType() == 1) {
            logger.info(">>> 更新资源域名");
            logger.info("操作域名：{}", curDomain);
            handleResourceDomain(domainInfo,curDomain);
            return;
        }
        // 对使用中的域名进行操作
        if(curDomain.getStatus().equals(inuseStatus)) {
            if(domainInfo.getStatus().equals(invalidStatus)) {
                // 使用中->失效
                // 1. 更新待操作域名到失效状态
                baseMapper.updateById(domainInfo);
                // 2. 从在用状态域名中随机选择一个
                Integer inuseSize = baseMapper.getInUseDomainSize();
                if(inuseSize < domainPoolSize) {
                    pushReadyDomainToInUse(domainPoolSize - inuseSize);
                }
                List<DomainInfo> inUseDms = getInUseDomain();
                if(inUseDms == null || inUseDms.size() == 0) {
                    //throw new GunsException(BizExceptionEnum.DOMAIN_IS_EXHAUST);
                    return;
                }
                System.out.println(inUseDms);
                Collections.shuffle(inUseDms);
                DomainInfo toUseDm = inUseDms.get(0);
                // 3. 更新所有使用待操作域名的应用落地页域名
                applicationService.updateApplicationWithDomainName(curDomain.getUrlName(), toUseDm.getUrlName());


            } else if(domainInfo.getStatus().equals(readyStatus)) {
                // 不允许使用中转备用
                throw new GunsException(BizExceptionEnum.DOMAIN_OPERATE_NOT_SUPPORT);
            }
            return;
        }
        // 对待用域名操作
        if(curDomain.getStatus().equals(readyStatus)) {
            baseMapper.updateById(domainInfo);
        }
    }

    public void handleResourceDomain(DomainInfo domainInfo, DomainInfo curDomain) {
        // 只允许备用转在用 或者 在用转失效 或者 备用转失效
        logger.info("domainInfo:{}", domainInfo);
        logger.info("curDomain:{}", curDomain);
        if(curDomain.getStatus().equals(readyStatus) && domainInfo.getStatus().equals(invalidStatus)) {
            // 备用转失效
            DomainInfo qDomain = new DomainInfo();
            qDomain.setId(curDomain.getId());
            qDomain.setType(1);
            DomainInfo targetDomain = baseMapper.selectOne(qDomain);
            if(targetDomain != null) {
                targetDomain.setStatus(invalidStatus);
                baseMapper.updateById(targetDomain);
            }
            return;
        }
        if(curDomain.getStatus().equals(inuseStatus)
                && domainInfo.getStatus().equals(invalidStatus)) {
            // 在用转失效
            List<DomainInfo> dms = baseMapper.getBakResourceDomain();
            // 无备用用域名异常
            if(dms == null || dms.size() == 0) {
                throw new GunsException(BizExceptionEnum.DOMAIN_IS_EXHAUST);
            }
            // 将一个备用的域名置为在用，将当前域名置为失效
            DomainInfo cDms = dms.get(0);
            cDms.setStatus(inuseStatus);
            baseMapper.updateById(cDms);
            curDomain.setStatus(invalidStatus);
            baseMapper.updateById(curDomain);
            logger.info("更新plist域名，老域名: {}, 新域名: {}",
                    curDomain.getUrlName(), cDms.getUrlName());
            updateResourcePlist(curDomain.getUrlName() ,cDms.getUrlName());
            signTasksMapper.updateSignOutputDomain(curDomain.getUrlName() ,cDms.getUrlName());
            applicationMapper.updateAllDomainOfApplication(curDomain.getUrlName() ,cDms.getUrlName());
            return;
        }
        if(curDomain.getStatus().equals(readyStatus) && domainInfo.getStatus().equals(inuseStatus)) {
            // 备用转在用
            // 将当前在用的域名改为失效
            DomainInfo qDomain = new DomainInfo();
            qDomain.setStatus(inuseStatus);
            qDomain.setType(1);
            DomainInfo inuseDomain = baseMapper.selectOne(qDomain);
            if(inuseDomain != null) {
                inuseDomain.setStatus(invalidStatus);
                baseMapper.updateById(inuseDomain);
            }
            curDomain.setStatus(inuseStatus);
            baseMapper.updateById(curDomain);
            if(inuseDomain != null) {
                logger.info("更新plist域名，老域名: {}, 新域名: {}",
                        inuseDomain.getUrlName(), curDomain.getUrlName());
                updateResourcePlist(inuseDomain.getUrlName() ,curDomain.getUrlName());
                signTasksMapper.updateSignOutputDomain(inuseDomain.getUrlName() ,curDomain.getUrlName());
                applicationMapper.updateAllDomainOfApplication(inuseDomain.getUrlName() ,curDomain.getUrlName());
            }
            return;
        }
        throw new GunsException(BizExceptionEnum.DOMAIN_OPERATE_NOT_SUPPORT);

    }

    public void createNewDomain(DomainInfo domainInfo) {
        DomainInfo qDomain = new DomainInfo();
        qDomain.setUrlName(domainInfo.getUrlName());
        DomainInfo extDomain = baseMapper.selectOne(qDomain);
        if(extDomain != null) {
            throw new GunsException(BizExceptionEnum.DOMAIN_NAME_HAS_EXIST);
        }
        super.insert(domainInfo);
    }

    public void updateResourcePlist(String oldDomain,String newDomain) {
        List<String> appCodes = baseMapper.getAllAvailableAppCodes();
        for(String code: appCodes) {
            File fs = new File(rootPath + code);
            if(fs.exists()) {
                for(File ff:fs.listFiles()) {
                    if(ff.isDirectory()) {
                        String plistFile = ff.getPath() + File.separatorChar + "download.plist";
                        if(!new File(plistFile).exists()) {
                            continue;
                        }
                        try {
                            updatePlistFileOfPath(plistFile,oldDomain, newDomain);
                        } catch (Exception e){
                            System.out.println("改写plist失败");
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

    }

    // 更新下载plist文件
    public void updatePlistFileOfPath(String sourcePath,String oldDomain, String newDomain) throws Exception {
        System.out.println("start write plist path sourcePath:" + sourcePath);
        SAXReader saxReader = new SAXReader();
        saxReader.setEncoding("UTF-8");
        Document document = saxReader.read(new File(sourcePath));
        Element root = document.getRootElement();

        Element dictElement = root.element("dict").element("array").element("dict").element("array");
        String type = "";
        Iterator iterator = dictElement.elementIterator();
        while (iterator.hasNext()) {
            Element element = (Element) iterator.next();
            Iterator childElement = element.elementIterator();
            while (childElement.hasNext()) {
                Element child = (Element) childElement.next();
                if (child.getName().equals("key") && child.getStringValue().equals("kind")) {
                    type = ((Element) childElement.next()).getStringValue();
                }
                if (child.getName().equals("key") && child.getStringValue().equals("url")) {
                    if (type.equals("software-package")) {
                        // 包地址
                        Element spNode = (Element) childElement.next();
                        String oldIpaUrl = spNode.getText();
                        if(oldIpaUrl.startsWith(oldDomain)) {
                            String newIpaUrl = oldIpaUrl.replace(oldDomain, newDomain);
                            spNode.setText(newIpaUrl);
                        }
                    } else if (type.equals("display-image")) {
                        // 图片
                        Element diNode = (Element) childElement.next();
                        String oldImgUrl = diNode.getText();
                        if(oldImgUrl.startsWith(oldDomain)) {
                            String newImgUrl = oldImgUrl.replace(oldDomain,newDomain);
                            diNode.setText(newImgUrl);
                        }

                    } else if (type.equals("full-size-image")) {
                        // faImage
                        Element faNode = (Element) childElement.next();
                        String oldFaImageUrl = faNode.getText();
                        if(oldFaImageUrl.startsWith(oldDomain)) {
                            String newFaImageUrl = oldFaImageUrl.replace(oldDomain,newDomain);
                            faNode.setText(newFaImageUrl);
                        }
                    }
                }
            }
        }

        OutputFormat format = new OutputFormat();
        format.setEncoding("UTF-8");
        format.setNewlines(true);
        format.setIndent("    ");
        FileOutputStream out = new FileOutputStream(new File(sourcePath));

        XMLWriter writer = new XMLWriter(out, format);
        writer.write(document);
        writer.flush();
        writer.close();
    }
}
